﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Elasticsearch.Net;
using FortuneUniverse.Fortune.Dyson;

// ReSharper disable MemberCanBePrivate.Global

namespace GenData
{
    public static class Program
    {
        public const int STEP = 4000;
        public const int START = 0;
        public const int DATA_COUNT = 25;
        public const int END = START + STEP * DATA_COUNT;

        public static readonly ProcessBar PROCESS_BAR = new ProcessBar(END - START, 0);
        public static ElasticLowLevelClient client;
        public static readonly List<object> FAIL_LIST = new List<object>();
        
        // ReSharper disable once InconsistentNaming
        public static void Main(string[] args)
        {
            initUniverseGen();
            initElasticSearchClient();
            Stopwatch sw = new Stopwatch();
            sw.Start();

            Queue<GenDataJob> genDataJobs = new Queue<GenDataJob>(DATA_COUNT);
            for (int i = START; i < END; i += STEP)
            {
                genDataJobs.Enqueue(new GenDataJob
                {
                    jobContext = new JobContext
                    {
                        seed = i,
                        step = STEP
                    }
                });
            }

            while (genDataJobs.Count > 0)
            {
                GenDataJob genDataJob = genDataJobs.Dequeue();
                List<object> galaxyDataList = genDataJob.execute();

                Task<BytesResponse> task = client.BulkAsync<BytesResponse>(PostData.MultiJson(galaxyDataList));
                ThreadPool.QueueUserWorkItem(getTaskResult, new TaskContext
                {
                    task = task,
                    seed = genDataJob.jobContext.seed,
                    step = genDataJob.jobContext.step
                });
                lock (FAIL_LIST)
                {
                    long milliseconds = sw.ElapsedMilliseconds;
                    PROCESS_BAR.updateCount(STEP, $"{formatTime(milliseconds)}{genAvgCost(sw.ElapsedMilliseconds, PROCESS_BAR.count)} | 步长:{STEP} | 当前失败段数{FAIL_LIST.Count}");
                }

                Thread.Sleep(400);
                GC.Collect();
            }

            Console.Out.WriteLine("\n数据插入完毕，失败情况：");

            lock (FAIL_LIST)
            {
                if (FAIL_LIST.Count == 0)
                {
                    Console.Out.WriteLine("无");
                }
                else
                {
                    foreach (object obj in FAIL_LIST)
                    {
                        Console.Out.WriteLine(obj);
                    }
                }
            }

            Console.Out.Write("按任意键退出...");
            Console.ReadKey();
        }

        private static string genAvgCost(long milliseconds, long count)
        {
            if (count == 0)
            {
                return "";
            }

            long avgCost = milliseconds * STEP / count;
            return $" | 轮均:{avgCost}ms";
        }

        private static void getTaskResult(object task)
        {
            TaskContext context = (TaskContext) task;
            Task<BytesResponse> bulkAsync = context.task;
            while (!bulkAsync.IsCompleted)
            {
                Thread.Sleep(50);
            }

            if (bulkAsync.Result.Success) return;
            lock (FAIL_LIST)
            {
                Console.Out.WriteLine(Encoding.Default.GetString(bulkAsync.Result.Body));
                FAIL_LIST.Add(context.seed);
            }
        }

        private static string formatTime(long milliseconds)
        {
            long hour = milliseconds / 3600000;
            long minute = (milliseconds / 60000) % 60;
            long second = (milliseconds / 1000) % 60;
            long milli = milliseconds % 1000;
            return $"{hour.ToString().PadLeft(2, '0')}:{minute.ToString().PadLeft(2, '0')}:{second.ToString().PadLeft(2, '0')}.{milli.ToString().PadLeft(3, '0')}";
        }

        private static void initElasticSearchClient()
        {
            Uri[] uris =
            {
                new Uri("http://192.168.0.108:9200"),
                new Uri("http://192.168.0.108:9201"),
                new Uri("http://192.168.0.108:9202"),
            };

            SniffingConnectionPool connectionPool = new SniffingConnectionPool(uris);
            ConnectionConfiguration settings =
                new ConnectionConfiguration(connectionPool).RequestTimeout(TimeSpan.FromMinutes(2));

            client = new ElasticLowLevelClient(settings);
            Console.Out.WriteLine("已初始化ES连接");
        }

        private static void initUniverseGen()
        {
            LDB.items.OnAfterDeserialize();
            LDB.veins.OnAfterDeserialize();
            LDB.themes.OnAfterDeserialize();
            PlanetModelingManager.Start();
        }
    }

    public class TaskContext
    {
        public Task<BytesResponse> task { get; set; }
        public int seed { get; set; }
        public int step { get; set; }
    }
}